﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace MetaSharp.Transformation.Patterns
{
    class TransformPattern : Pattern
    {
        private static PatternMatch ErrorUntilNotAny = ErrorUntil(Not(Any())).Compile();
        public IImmutableArray<Pattern> Patterns { get; private set; }

        public TransformPattern(params Pattern[] patterns)
            : this(new ArrayList<Pattern>(patterns))
        {
        }

        public TransformPattern(IImmutableArray<Pattern> patterns)
            : base(PatternKind.Transform)
        {
            this.Patterns = patterns;
        }

        public override PatternMatch Compile()
        {
            var patterns = new List<PatternMatch>();
            foreach (var pattern in Patterns)
                patterns.Add(pattern.Compile());

            return s => Target(s, patterns);
        }

        private static async Task<IMatch> Target(IScope scope, IList<PatternMatch> patterns)
        {
            IMatch match = null;
            var i = patterns.Count;
            for (int x = 0; x < i; x++)
            {
                match = await patterns[x](scope);

                if (!match.End.Stream.IsEmpty)
                {
                    var m = await ErrorUntilNotAny(match.End);
                    match = match
                        .AddErrors(m.Errors)
                        .SetEnd(m.End);
                }

                foreach (var error in match.Errors)
                {
                    if (error.Severity == ErrorSeverity.Error)
                    {
                        var m = await MetaSharp.Transformation.Match.Fail(match.End);
                        match = m.AddErrors(match.Errors);
                        break;
                    }
                }

                if (!match.Matched)
                    return match;

                if (x < i - 1)
                {
                    scope = match.Step();
                }
            }

            return match;
        }

    }
}
